﻿using Engine;
using Engine.AddinManager;
using Engine.FileFormats3D;
using Engine.Graphics;
using Engine.Graphics.VertexStructures;
using SharpDX;
using System.Collections.Generic;

namespace street
{
    internal class CreateRailMesh
    {
        #region Public Methods

        public static Mesh3dData CreateGuardRail(Vector3[] middle, decimal anglePointOne, decimal anglePointTwo, float textureUcoordinates, Material materialID_ForRail, Engine.AddinManager.AddinManager addinManager, IAddin addin, street.GameDllInterfaces.IStreetAddin addinForVertexCreation)
        {
            List<ushort> indices = new List<ushort>();
            List<MyOwnVertex.PositionNormalTextured> vertices = new List<MyOwnVertex.PositionNormalTextured>();
            List<IGameVertex> verticesWr2Data = new List<IGameVertex>();

            List<Material> materialIDs = new List<Material>();

            Vector3[] middleLine = MiddleLine.CreateNewMiddleLineSegments(middle, 300.0f, 7.0f);
            Vector3[] left = MiddleLine.CreateNewMiddleLine(middleLine, anglePointOne, anglePointTwo, -1.25f, -1.25f, 0, 0, 0);
            Vector3[] left2 = MiddleLine.CreateNewMiddleLine(middleLine, anglePointOne, anglePointTwo, -1.0f, -1.0f, 0, 0, 0);
            Vector3[] rightTemp = MiddleLine.CreateNewMiddleLine(middleLine, anglePointOne, anglePointTwo, 0.0f, 0.0f, 0, 0, 0);

            //Reverse the right lane
            Vector3[] right = new Vector3[rightTemp.Length];
            for (int i = 0; i < right.Length; i++)
            {
                right[right.Length - (i + 1)] = rightTemp[i];
            }

            //left and right side
            List<int> polysPerTexture = new List<int>();
            polysPerTexture.Add(0);
            CreateGuardRailPart1(ref left, ref vertices, ref verticesWr2Data, ref indices, ref polysPerTexture, anglePointOne, anglePointTwo, 14.0f, 14.0f, -90, -90, textureUcoordinates, addinForVertexCreation);

            Vector3[] leftReverse = new Vector3[left2.Length];
            for (int i = 0; i < leftReverse.Length; i++)
            {
                leftReverse[leftReverse.Length - (i + 1)] = left2[i];
            }

            CreateGuardRailPart1(ref leftReverse, ref vertices, ref verticesWr2Data, ref indices, ref polysPerTexture, anglePointOne, anglePointTwo, 14.0f, 14.0f, -90, -90, textureUcoordinates, addinForVertexCreation);
            CreateGuardRailPart2(ref right, ref vertices, ref verticesWr2Data, ref indices, ref polysPerTexture, anglePointOne, anglePointTwo, 10, 10, -90, -90, textureUcoordinates, addinForVertexCreation);

            //Top
            CreateGuardRailPart3(ref middleLine, ref vertices, ref verticesWr2Data, ref indices, ref polysPerTexture, anglePointOne, anglePointTwo, 1.25f, 1.25f, 0, 0, textureUcoordinates, addinForVertexCreation);

            materialIDs.Add(materialID_ForRail);

            return new Mesh3dData(indices, vertices, verticesWr2Data, polysPerTexture, materialIDs, addinManager, addin);//, mappingMode);
        }

        #endregion Public Methods

        #region Private Methods

        private static void CreateGuardRailPart1(ref Vector3[] middle, ref List<MyOwnVertex.PositionNormalTextured> vertices,
              ref List<IGameVertex> verticesWr2Data, ref List<ushort> indices, ref List<int> polyTextures,
              decimal anglePointOne, decimal anglePointTwo,
              float widthPointOne, float widthPointTwo,
              float inclinationPointOne, float inclinationPointTwo, float textureUcoordinate, street.GameDllInterfaces.IStreetAddin addinForVertexCreation)
        {
            short OffsetIndices = (short)(vertices.Count);
            polyTextures[0] += ((middle.Length - 1) * 2);

            // This calculates the distances between each Vectors from middleline
            float[] middleLineDistances = new float[middle.Length];
            float lengthOfStreet = 0;
            middleLineDistances[0] = GeoMath.DistanceBtw2Points(middle[0], middle[1]);
            lengthOfStreet += middleLineDistances[0];
            for (int i2 = 1; i2 < middle.Length - 1; i2++)
            {
                middleLineDistances[i2] = GeoMath.DistanceBtw2Points(middle[i2], middle[i2 + 1]);
                lengthOfStreet += middleLineDistances[i2];
            }

            // Calculate V-Texturecoordinates
            float[] textureUcoordinates = new float[middle.Length];
            for (int i = 1; i < textureUcoordinates.Length; i++)
            {
                textureUcoordinates[i] = textureUcoordinate;
                textureUcoordinate += middleLineDistances[i] / 100.0f;
            }

            // here will the indicies calculate
            short point = 0;
            for (short i2 = 0; i2 < (middle.Length - 1) * 6; i2 += 6)
            {
                indices.Add((ushort)(point + OffsetIndices));
                indices.Add((ushort)(point + 1 + OffsetIndices));
                indices.Add((ushort)(point + 2 + OffsetIndices));

                indices.Add((ushort)(point + 1 + OffsetIndices));
                indices.Add((ushort)(point + 3 + OffsetIndices));
                indices.Add((ushort)(point + 2 + OffsetIndices));

                point += 2;
            }

            Vector3 rightPoint; //Is a tempoary Point
            float percentofStreet = 0;
            float streetwidth;
            float inclination = 0;

            //start
            rightPoint = GeoMath.MakePointFromAngel(180 - anglePointOne, widthPointOne, inclinationPointOne);

            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[0],
                Normal = Vector3.Zero,
                UV = new Vector2(textureUcoordinates[0], 0.25f)
            });

            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[0] + rightPoint,
                Normal = Vector3.Zero,
                UV = new Vector2(textureUcoordinates[0], 1.0f)
            });

            for (int i = 1; i < (middle.Length - 1); i++)
            {
                percentofStreet += middleLineDistances[i - 1];
                streetwidth = widthPointOne - ((widthPointOne - widthPointTwo) * (percentofStreet / lengthOfStreet));
                inclination = inclinationPointOne + ((inclinationPointTwo - inclinationPointOne) * (percentofStreet / lengthOfStreet));

                rightPoint = GeoMath.makepoint(middle[i + 1], middle[i - 1], streetwidth, inclination);

                vertices.Add(new MyOwnVertex.PositionNormalTextured()
                {
                    Position = middle[i],
                    Normal = Vector3.Zero,
                    UV = new Vector2(textureUcoordinates[i], 0.25f)
                });

                vertices.Add(new MyOwnVertex.PositionNormalTextured()
                {
                    Position = middle[i] + rightPoint,
                    Normal = Vector3.Zero,
                    UV = new Vector2(textureUcoordinates[i], 1.0f)
                });
            }
            rightPoint = GeoMath.MakePointFromAngel(180 - anglePointTwo - 180, widthPointTwo, inclinationPointTwo);

            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[middle.Length - 1],
                Normal = Vector3.Zero,
                UV = new Vector2(textureUcoordinates[textureUcoordinates.Length - 1], 0.25f)
            });

            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[middle.Length - 1] + rightPoint,
                Normal = Vector3.Zero,
                UV = new Vector2(textureUcoordinates[textureUcoordinates.Length - 1], 1.0f)
            });

            addinForVertexCreation.CreateGameVerticesGuardRailPart1(middle, verticesWr2Data, textureUcoordinates);
        }

        private static void CreateGuardRailPart2(ref Vector3[] middle, ref List<MyOwnVertex.PositionNormalTextured> vertices, ref List<IGameVertex> verticesWr2Data,
            ref List<ushort> indices, ref List<int> polyTextures, decimal anglePointOne, decimal anglePointTwo, float widthPointOne, float widthPointTwo,
            float inclinationPointOne, float inclinationPointTwo, float textureUcoordinate, street.GameDllInterfaces.IStreetAddin addinForVertexCreation)
        {
            short OffsetIndices = (short)(vertices.Count);
            polyTextures[0] += ((middle.Length - 1) * 2);

            // This calculates the distances between each Vectors from middleline
            float[] middleLineDistances = new float[middle.Length];
            float lengthOfStreet = 0;
            middleLineDistances[0] = GeoMath.DistanceBtw2Points(middle[0], middle[1]);
            lengthOfStreet += middleLineDistances[0];
            for (int i2 = 1; i2 < middle.Length - 1; i2++)
            {
                middleLineDistances[i2] = GeoMath.DistanceBtw2Points(middle[i2], middle[i2 + 1]);
                lengthOfStreet += middleLineDistances[i2];
            }

            float[] textureUcoordinates = new float[middle.Length];
            for (int i = 0; i < textureUcoordinates.Length; i++)
            {
                textureUcoordinates[i] = textureUcoordinate;
                textureUcoordinate += middleLineDistances[i] / 100.0f;
            }

            // here will the indicies calculate
            short point = 0;
            for (short i2 = 0; i2 < (middle.Length - 1) * 6; i2 += 6)
            {
                indices.Add((ushort)(point + OffsetIndices));
                indices.Add((ushort)(point + 1 + OffsetIndices));
                indices.Add((ushort)(point + 2 + OffsetIndices));

                indices.Add((ushort)(point + 1 + OffsetIndices));
                indices.Add((ushort)(point + 3 + OffsetIndices));
                indices.Add((ushort)(point + 2 + OffsetIndices));

                point += 2;
            }

            Vector3 rightPoint; //Is a tempoary Point
            float percentofStreet = 0;
            float streetwidth;
            float inclination = 0;

            //start
            rightPoint = GeoMath.MakePointFromAngel(180 - anglePointOne, widthPointOne, inclinationPointOne);

            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[0],
                Normal = Vector3.Zero,
                UV = new Vector2(textureUcoordinates[0], 0.25f)
            });

            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[0] + rightPoint,
                Normal = Vector3.Zero,
                UV = new Vector2(textureUcoordinates[0], 1.0f)
            });

            for (int i = 1; i < (middle.Length - 1); i++)
            {
                percentofStreet += middleLineDistances[i - 1];
                streetwidth = widthPointOne - ((widthPointOne - widthPointTwo) * (percentofStreet / lengthOfStreet));
                inclination = inclinationPointOne + ((inclinationPointTwo - inclinationPointOne) * (percentofStreet / lengthOfStreet));

                rightPoint = GeoMath.makepoint(middle[i + 1], middle[i - 1], streetwidth, inclination);

                vertices.Add(new MyOwnVertex.PositionNormalTextured()
                {
                    Position = middle[i],
                    Normal = Vector3.Zero,
                    UV = new Vector2(textureUcoordinates[i], 0.25f)
                });

                vertices.Add(new MyOwnVertex.PositionNormalTextured()
                {
                    Position = middle[i] + rightPoint,
                    Normal = Vector3.Zero,
                    UV = new Vector2(textureUcoordinates[i], 0.5f)
                });
            }

            rightPoint = GeoMath.MakePointFromAngel(180 - anglePointTwo - 180, widthPointTwo, inclinationPointTwo);

            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[middle.Length - 1],
                Normal = Vector3.Zero,
                UV = new Vector2(textureUcoordinates[textureUcoordinates.Length - 1], 0.25f)
            });

            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[middle.Length - 1] + rightPoint,
                Normal = Vector3.Zero,
                UV = new Vector2(textureUcoordinates[textureUcoordinates.Length - 1], 0.5f)
            });

            addinForVertexCreation.CreateGameVerticesGuardRailPart2(middle, verticesWr2Data, textureUcoordinates);
        }

        private static void CreateGuardRailPart3(ref Vector3[] middle, ref List<MyOwnVertex.PositionNormalTextured> vertices, ref List<IGameVertex> verticesWr2Data,
            ref List<ushort> indices, ref List<int> polyTextures, decimal anglePointOne, decimal anglePointTwo, float widthPointOne, float widthPointTwo,
            float inclinationPointOne, float inclinationPointTwo, float textureUcoordinate, street.GameDllInterfaces.IStreetAddin addinForVertexCreation)
        {
            short OffsetIndices = (short)(vertices.Count);
            polyTextures[0] += ((middle.Length - 1) * 2);

            // This calculates the distances between each Vectors from middleline
            float[] middleLineDistances = new float[middle.Length];
            float lengthOfStreet = 0;
            middleLineDistances[0] = GeoMath.DistanceBtw2Points(middle[0], middle[1]);
            lengthOfStreet += middleLineDistances[0];
            for (int i2 = 1; i2 < middle.Length - 1; i2++)
            {
                middleLineDistances[i2] = GeoMath.DistanceBtw2Points(middle[i2], middle[i2 + 1]);
                lengthOfStreet += middleLineDistances[i2];
            }

            float[] textureUcoordinates = new float[middle.Length];
            for (int i = 0; i < textureUcoordinates.Length; i++)
            {
                textureUcoordinates[i] = textureUcoordinate;
                textureUcoordinate += middleLineDistances[i] / 100.0f;
            }

            // here will the indicies calculate
            short point = 0;
            for (short i2 = 0; i2 < (middle.Length - 1) * 6; i2 += 6)
            {
                indices.Add((ushort)(point + OffsetIndices));
                indices.Add((ushort)(point + 1 + OffsetIndices));
                indices.Add((ushort)(point + 2 + OffsetIndices));

                indices.Add((ushort)(point + 1 + OffsetIndices));
                indices.Add((ushort)(point + 3 + OffsetIndices));
                indices.Add((ushort)(point + 2 + OffsetIndices));

                point += 2;
            }

            Vector3 rightPoint; //Is a tempoary Point
            float percentofStreet = 0;
            float streetwidth;
            float inclination = 0;

            //start
            rightPoint = GeoMath.MakePointFromAngel(180 - anglePointOne, widthPointOne, inclinationPointOne);

            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[0],
                Normal = Vector3.Zero,
                UV = new Vector2(0.5f, 0.5f)
            });

            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[0] + rightPoint,
                Normal = Vector3.Zero,
                UV = new Vector2(0.5f, 0.5f)
            });

            for (int i = 1; i < (middle.Length - 1); i++)
            {
                percentofStreet += middleLineDistances[i - 1];
                streetwidth = widthPointOne - ((widthPointOne - widthPointTwo) * (percentofStreet / lengthOfStreet));
                inclination = inclinationPointOne + ((inclinationPointTwo - inclinationPointOne) * (percentofStreet / lengthOfStreet));

                rightPoint = GeoMath.makepoint(middle[i + 1], middle[i - 1], streetwidth, inclination);

                vertices.Add(new MyOwnVertex.PositionNormalTextured()
                {
                    Position = middle[i],
                    Normal = Vector3.Zero,
                    UV = new Vector2(0.5f, 0.5f)
                });

                vertices.Add(new MyOwnVertex.PositionNormalTextured()
                {
                    Position = middle[i] + rightPoint,
                    Normal = Vector3.Zero,
                    UV = new Vector2(0.5f, 0.5f)
                });
            }

            rightPoint = GeoMath.MakePointFromAngel(180 - anglePointTwo - 180, widthPointTwo, inclinationPointTwo);

            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[middle.Length - 1],
                Normal = Vector3.Zero,
                UV = new Vector2(0.5f, 0.5f)
            });

            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[middle.Length - 1] + rightPoint,
                Normal = Vector3.Zero,
                UV = new Vector2(0.5f, 0.5f)
            });

            addinForVertexCreation.CreateGameVerticesGuardRailPart3(middle, verticesWr2Data);
        }

        #endregion Private Methods
    }
}